package net.dotool.geo;

import java.awt.Color;

import net.dotool.NumberTools;
import net.dotool.StringTools;


public final class GeoUtils {
	private static final double EARTH_RADIUS = 6370997D;

	public static final int DISTANCE_G = 150;
	public static final int DISTANCE_D = 80;
	public static final int DISTANCE_M = 50;

	// public static final double EARTH_RADIUS = 6378137.0;
	public static final double SCALE_CENTRALMERIDIAN = 0.9996;

	public static int calculateFxj(final int fxj, final int sep, final int jiaodu) {
		int calculateFxj1 = 0;
		if (sep == 0) {
			calculateFxj1 = fxj - jiaodu;
		} else {
			calculateFxj1 = fxj + jiaodu;
		}
		if (calculateFxj1 < 0) {
			calculateFxj1 = 360 - Math.abs(calculateFxj1);
		} else if (calculateFxj1 > 360) {
			calculateFxj1 = calculateFxj1 - 360;
		}
		return calculateFxj1;
	}

	public static int calculateFxj(final int fxj, final int sep) {
		return GeoUtils.calculateFxj(fxj, sep, 10);
	}

	/**
	 * 判断给定的Zone字符标识是否属于南半球
	 * 
	 * @param zoneLetter
	 * @return
	 */
	public static final boolean isSouthernHemisphere(final char zoneLetter) {
		// Correct y for southern hemisphere
		if (zoneLetter - 'N' < 0)
			return true;
		return false;
	}

	public static final char getLatitudeZone(final double latitude) {
		return UTMRef.getUTMLatitudeZoneLetter(latitude);
	}

	public static final int getLongitudeZone(final double latitude, final double longitude) {
		return LatLng.getLongitudeZone(latitude, longitude);
	}

	public static final UTMRef latLong2UTM(final double latitude, final double longitude) {
		return new LatLng(latitude, longitude).toUTMRef();
	}

	public static final LatLng utm2LatLng(final int lngZone, final char latZone, final double easting, final double northing) {
		return new UTMRef(lngZone, latZone, easting, northing).toLatLng();
	}

	/**
	 * 根据两个点的经纬度计算这两个点之间的距离
	 * 
	 * @param wd1
	 *            点1的纬度
	 * @param jd1
	 *            点1的经度
	 * @param wd2
	 *            点2的纬度
	 * @param jd2
	 *            点2的经度
	 * @return 点1和点2之间的距离,单位:公里
	 */
	public final static double distance(final double wd1, final double jd1, final double wd2, final double jd2) {
		return GeoUtils.distanceAsMeters(wd1, jd1, wd2, jd2) / 1000;
	}

	public final static double distanceAsMeters(final int lngZone, final char latZone, double lat1, double lon1, double lat2, double lon2) {
		LatLng latLng = GeoUtils.utm2LatLng(lngZone, latZone, lon1, lat1);
		lat1 = latLng.getLatitude();
		lon1 = latLng.getLongitude();

		latLng = GeoUtils.utm2LatLng(lngZone, latZone, lon2, lat2);
		lat2 = latLng.getLatitude();
		lon2 = latLng.getLongitude();

		return GeoUtils.distanceAsMeters(lat1, lon1, lat2, lon2);
	}

	public final static double distanceAsMeters(double lat1, double lon1, double lat2, double lon2) {
		try {
			GeoUtils.validationLatLong(lat1, lon1);
			GeoUtils.validationLatLong(lat2, lon2);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		lon1 = Math.toRadians(lon1);
		lon2 = Math.toRadians(lon2);
		lat1 = Math.toRadians(lat1);
		lat2 = Math.toRadians(lat2);
		return GeoUtils.EARTH_RADIUS * Math.acos(Math.sin(lat1) * Math.sin(lat2) + Math.cos(lat1) * Math.cos(lat2) * Math.cos(lon1 - lon2));
	}

	public final static boolean validationLatLong(final double latitude, final double longitude) {
		if (longitude < -180.0 || 180.0 <= longitude) {
			throw new IllegalArgumentException("The longitude is out of range  [-180, 180).");
		}
		if (latitude < -90.0 || 90.0 < latitude) {
			throw new IllegalArgumentException("The latitude  is out of range  [-90, 90].");
		}
		return true;
	}

	/**
	 * 根据3个点的经纬度值来计算其中指定点的角度值,point1 和 point2组成第一条边,point2 和 point3组成第二条边,point3 和 point1 组成第三条边.
	 * 第一条边和第三条边的交点称为顶点1,第一条边和第二条边的交点称为顶点2,第二条边和第三条边的交点称为顶点3
	 * 
	 * @param calculatePoint
	 *            指定要计算的顶点,值只能为1,2,3 ,并且顺序是直接和后面的参数点一致的.
	 * @param point1Jd
	 *            第一个点的经度
	 * @param point1Wd
	 *            第一个点的纬度
	 * @param point2Jd
	 *            第二个点的经度
	 * @param point2Wd
	 *            第二个点的纬度
	 * @param point3Jd
	 *            第三个点的经度
	 * @param point3Wd
	 *            第三个点的纬度
	 * @return
	 */
	public static final double calculateJiaoDu(final double calculatePoint, final double point1Jd, final double point1Wd, final double point2Jd,
			final double point2Wd, final double point3Jd, final double point3Wd) {
		// 第一条边的长度
		final double side1Len = GeoUtils.distanceAsMeters(point1Wd, point1Jd, point2Wd, point2Jd);
		// 第二条边的长度
		final double side2Len = GeoUtils.distanceAsMeters(point2Wd, point2Jd, point3Wd, point3Jd);
		// 第三条边的长度
		final double side3Len = GeoUtils.distanceAsMeters(point3Wd, point3Jd, point1Wd, point1Jd);
		return GeoUtils.calculateJiaoDu(calculatePoint, side1Len, side2Len, side3Len);
	}

	/**
	 * 根据已知的三角形的三条边来计算其中某个顶点的角度 第一条边和第三条边的交点称为顶点1,第一条边和第二条边的交点称为顶点2,第二条边和第三条边的交点称为顶点3
	 * 
	 * @param calculatePoint
	 *            指定要计算的顶点,值只能为1,2,3 ,并且顺序是直接和后面的参数点一致的.
	 * @param side1Len
	 * @param side2Len
	 * @param side3Len
	 * @return
	 */
	public static final double calculateJiaoDu(final double calculatePoint, final double side1Len, final double side2Len, final double side3Len) {
		// 指定的要计算的角度点只能是1,2,3
		if (calculatePoint < 1 || calculatePoint > 3) {
			throw new IllegalArgumentException("指定的计算点不能大于3并且不能小于1:" + calculatePoint);
		}
		/*
		 * A=acos(((b^2 + c^2) - a^2)/2bc) B=acos(((a^2 + c^2) - b^2)/2ac) C=acos(((a^2 + b^2) - c^2)/2ab)
		 */
		double hudu = 0;
		if (calculatePoint == 1) {
			hudu = Math.acos((side1Len * side1Len + side3Len * side3Len - side2Len * side2Len) / (2 * side1Len * side3Len));
		} else if (calculatePoint == 2) {
			hudu = Math.acos((side1Len * side1Len + side2Len * side2Len - side3Len * side3Len) / (2 * side1Len * side2Len));
		} else if (calculatePoint == 3) {
			hudu = Math.acos((side2Len * side2Len + side3Len * side3Len - side1Len * side1Len) / (2 * side2Len * side3Len));
		}
		return Math.toDegrees(hudu);
	}

	/**
	 * 通过基站和频段类型来获取基站颜色
	 * 
	 * @param jzlx
	 * @return
	 */
	public static Color getColorByJzlxAndPd(final String jzlx, final String pd) {
		Color color = new Color(255, 0, 0);
		if (jzlx.equals("微蜂窝")) {
			color = Color.green;
		} else if (pd.equals("900M")) {
			color = Color.red;
		} else if (pd.equals("1800M")) {
			color = Color.blue;
		}
		return color;
	}

	/**
	 * 通过频段来获取小区的长度
	 * 
	 * @param pd
	 * @return
	 */
	public static float getDistance(final String pd) {
		if (StringTools.isNotBlank(pd)) {
			if (pd.startsWith("900")) {// 900站 //黑 细
				return GeoUtils.DISTANCE_G;
			} else if (pd.startsWith("1800")) {// 1800站 //蓝粗
				return GeoUtils.DISTANCE_D;
			}
		}
		return GeoUtils.DISTANCE_G;
	}

	/**
	 * 通过频段和基站类型来获取连接点的位置
	 * 
	 * @param pd
	 * @param jzlx
	 * @param fxj
	 * @param jd
	 * @param wd
	 * @return
	 */
	public static double[] getLinkPoint(final String pd, final String jzlx, final int fxj, final double jd, final double wd) {
		if (jzlx.equals("微蜂窝")) {
			return new double[] { jd, wd };
		}
		final double cjd = jd + NumberTools.CaculateCos(fxj, GeoUtils.getDistance(pd) * 0.67f);
		final double cwd = wd + NumberTools.CaculateSin(fxj, GeoUtils.getDistance(pd) * 0.67f);
		return new double[] { cjd, cwd };
	}

	/**
	 * 获取某一个扇区的扇形点
	 * 
	 * @param jd
	 * @param wd
	 * @param fxj
	 * @param dis
	 * @return
	 */
	public static double[] getCellPoints(final double jd, final double wd, final int fxj, final float dis) {
		final double pts[] = new double[60];
		pts[0] = jd;
		pts[1] = wd;
		for (int i = 15; i > 0; i--) {
			final int leftFxj = GeoUtils.calculateFxj(fxj, 0, i * 2);
			final double leftJd = jd + NumberTools.CaculateCos(leftFxj, dis);
			final double leftWd = wd + NumberTools.CaculateSin(leftFxj, dis);
			pts[(15 - i) * 2 + 2] = leftJd;
			pts[(15 - i) * 2 + 3] = leftWd;
		}
		for (int i = 1; i < 15; i++) {
			final int leftFxj = GeoUtils.calculateFxj(fxj, 1, i * 2);
			final double leftJd = jd + NumberTools.CaculateCos(leftFxj, dis);
			final double leftWd = wd + NumberTools.CaculateSin(leftFxj, dis);
			pts[i * 2 + 30] = leftJd;
			pts[i * 2 + 31] = leftWd;
		}
		return pts;
	}

	public static void main(final String[] args) {
		final LatLng latLng = GeoUtils.utm2LatLng(50, 'T', 363596.38, 9366000);
		// (39.42802067233801, 118.90072056804968)
		// (39.43275375616301, 115.41508996080387)

		System.out.println(latLng);
	}
}
